package fnic.modesearch;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.ObjectMapper;

import fnic.jsontrans.SearchQueryJsonMode.*;
import fnic.jsontrans.SearchResultJsonMode.*;

public class SearchModeJsonParser {
	private static final Logger LOG = Logger.getLogger(SearchModeJsonParser.class);
	private static final ObjectMapper mapper = new ObjectMapper();
	private JsonNode rootnode; //单个查询请求{}
	private JsonNode[] roots;  //批量查询[]
	private boolean bulkfg = false;

	private int rootnum = 0;
	private int rootposition = 0;

	private boolean totalflg = false;
	private boolean keyflg = false;
	private boolean rankingflg = false;
	private boolean inputflg = false;
	private boolean outputflg = false;
	private boolean statflg = false;
	
	String key;
	String[] outfields = null;
//	String[] rankingfields = null;
	
	IdsQueryParam[] idsParam = null;
	KeyWordQueryParam[] kwsParam = null;
	PrefixQueryParam[] presParam = null;
	RegexQueryParam[] regexParam = null;
	RangeQueryParam[] rangesParam = null;
	GEOQueryParam[] geosParam =null;
	
	InTermFieldStat[] intermsStat = null;
	InTermsKVStat[]  inkvsStat = null;
	InNumFieldStat[] infieldStat = null;
	InRangesGroupStat[] inrangegroupsStat = null;
	
	

	SearchModeJsonGenerater resultjsongen;
	
	public SearchModeJsonParser(SearchModeJsonGenerater result){
		resultjsongen = result;
	}
	
	public boolean root(InputStream instream){
		try {
			rootnode = mapper.readTree(instream);
		} catch (JsonProcessingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
		
		if(rootnode.isArray()){
			bulkfg = true;
			rootnum = rootnode.size();			
			roots = new JsonNode[rootnum];
			
			int counter = 0;
			Iterator<JsonNode> rootitor = rootnode.iterator();
			while(rootitor.hasNext()){
				roots[counter++] = rootitor.next();
			}
		}
		return true;
	}
	
	/*
	 * InputStreamReader isr = new InputStreamReader(request.getInputStream(), "UTF-8");
	 * 指定UTF-8编码格式
	 * */
	public boolean root(Reader reader){
		try {
			rootnode = mapper.readTree(reader);
		} catch (JsonProcessingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
		
		if(rootnode.isArray()){
			bulkfg = true;
			rootnum = rootnode.size();			
			roots = new JsonNode[rootnum];
			
			int counter = 0;
			Iterator<JsonNode> rootitor = rootnode.iterator();
			while(rootitor.hasNext()){
				roots[counter++] = rootitor.next();
			}
		}
		return true;
	}
	
	public boolean root(String jstring){
		try {
			rootnode = mapper.readTree(jstring);
		} catch (JsonProcessingException e) {
			// TODO Auto-generated catch block
			LOG.error("json root parse error:" + e.getMessage());
			return false;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			LOG.error("io root parse error:" + e.getMessage());
			return false;
		}
		
		if(rootnode.isArray()){
			bulkfg = true;
			rootnum = rootnode.size();			
			roots = new JsonNode[rootnum];
			
			int counter = 0;
			Iterator<JsonNode> rootitor = rootnode.iterator();
			while(rootitor.hasNext()){
				roots[counter++] = rootitor.next();
			}
		}
		return true;
	}
	
	
	public boolean parse(){
		try {
			JsonNode totalhit = rootnode.get("needtotal");
			totalflg = parseTotalhits(totalhit);

			JsonNode keynode = rootnode.get("key");
			keyflg = parseAncherKey(keynode);

			JsonNode ranknode = rootnode.get("ranking");
			rankingflg = parseRanking(ranknode);

			JsonNode input = rootnode.get("input");
			inputflg = parseInput(input);

			JsonNode output = rootnode.get("output");
			outputflg = parseOutput(output);

			JsonNode stat = rootnode.get("statistic");
			statflg = parseStat(stat);
		} catch (Exception e) {
			LOG.error("mode search parse error:" + e.getMessage());
		}
		
		//保证input output statistic三者中至少存在一个
		return (rankingflg || inputflg || outputflg || statflg);
	}
	
	public boolean hasparse(){
		return (rootposition<rootnum)?true:false;
	}
	
	public boolean nextparse(){
		if (rootposition < rootnum) {
			rootnode = roots[rootposition++];
			return parse();
		} else {
			return false;
		}
	}
	
	private boolean parseTotalhits(JsonNode hitnodes){
		if(hitnodes!=null&&hitnodes.isBoolean()){
			return hitnodes.asBoolean();
		}else{
			return false;
		}
	}
	
	private boolean parseAncherKey(JsonNode keynode){
		if(keynode!=null&&keynode.isTextual()){
			key = keynode.asText();
			return true;
		}else{
			return false;
		}
	}
	
	private boolean parseInput(JsonNode inpputnodes){
		boolean inputfg = false;
		if(inpputnodes==null || !inpputnodes.isArray()){
			return false;
		}
		Iterator<JsonNode> nodeitor = inpputnodes.iterator();
		while(nodeitor.hasNext()){
			JsonNode node = nodeitor.next();
			if(!node.get("type").isTextual() || !node.get("content").isArray()){
				return false;
			}
			
			String type = node.get("type").asText();
			Iterator<JsonNode> content = node.get("content").iterator();
			int size = node.get("content").size();
					 
			if(type.equals("ids")){
				idsParam = new IdsQueryParam[size];
			}else if(type.equals("keyword")){
				kwsParam = new KeyWordQueryParam[size];
			} else if (type.equals("prefix")) {
				presParam = new PrefixQueryParam[size];
			} else if(type.equals("regex")){
				regexParam = new RegexQueryParam[size];
			} else if (type.equals("range")) {
				rangesParam = new RangeQueryParam[size];
			} else if (type.equals("position")) {
				geosParam = new GEOQueryParam[size];
			}else{
				return false;
			}

			int counter = 0;
			JsonNode contentItem;
			while (content.hasNext()) {
				contentItem = content.next();
				if (contentItem.isObject() && type.equals("ids")) {
					idsParam[counter] = new IdsQueryParam();
					idsParam[counter].setName(contentItem.get("name").asText());
					idsParam[counter].setValue(mutlivalueJsonTrans(contentItem.get("value")));
					counter++;
					inputfg = true;
				} else if (contentItem.isObject() && type.equals("keyword")) {
					kwsParam[counter] = new KeyWordQueryParam();
					kwsParam[counter].setName(contentItem.get("name").asText());
					kwsParam[counter].setValue(mutlivalueJsonTrans(contentItem.get("value")));
					counter++;
					inputfg = true;
				} else if (contentItem.isObject() && type.equals("prefix")) {
					presParam[counter] = new PrefixQueryParam();
					presParam[counter].setName(contentItem.get("name").asText());
					presParam[counter].setValue(mutlivalueJsonTrans(contentItem.get("value")));
					counter++;
					inputfg = true;
				} else if (contentItem.isObject() && type.equals("regex")) {
					regexParam[counter] = new RegexQueryParam();
					regexParam[counter].setName(contentItem.get("name").asText());
					regexParam[counter].setValue(mutlivalueJsonTrans(contentItem.get("value")));
					counter++;
					inputfg = true;
				} else if (contentItem.isObject() && type.equals("range")) {
					rangesParam[counter] = new RangeQueryParam();
					rangesParam[counter].setName(contentItem.get("name").asText());
					rangesParam[counter].setValue(rangeJsonTrans(contentItem.get("value")));
					counter++;
					inputfg = true;
				} else if (contentItem.isObject() && type.equals("position")) {
					geosParam[counter] = new GEOQueryParam();
					geosParam[counter].setName(contentItem.get("name").asText());
					geosParam[counter].setValue(positionJsonTrans(contentItem.get("value")));
					counter++;
					inputfg = true;
				}else{
					return false;
				}
			}
		}
		return inputfg;
	}
	
	private boolean parseOutput(JsonNode output){
		boolean outputfg = false;
		if(output==null || !output.isArray()){
			return false;
		}else{
			int outsize = output.size();
			resultjsongen.hitsoutput = new ArrayList<Map<String, Object>>(outsize);
			outfields = new String[outsize];
			Iterator<JsonNode> nodeitor = output.iterator();
			int counter = 0;
			while(nodeitor.hasNext()){
				JsonNode node = nodeitor.next();
				outfields[counter] = new String(node.asText());
				counter++;
				outputfg = true;
			}
			return outputfg;
		}
	}
	
	private boolean parseRanking(JsonNode rank){
		boolean rankfg = false;
		if(rank==null || !rank.isArray()){
			return false;
		}else{
			int outsize = rank.size();
			resultjsongen.ranking = new ArrayList<TypeScore>(outsize);
//			rankingfields = new String[outsize];
			Iterator<JsonNode> nodeitor = rank.iterator();
			int counter = 0;
			while(nodeitor.hasNext()){
				JsonNode node = nodeitor.next();
//				rankingfields[counter] = new String(node.asText());
				
				TypeScore tscore = new TypeScore();
				tscore.setType(node.asText());
				tscore.setScore(888888);
				resultjsongen.ranking.add(tscore);
				counter++;
				rankfg = true;
			}
			return rankfg;
		}
	}
	
	private boolean parseStat(JsonNode statnodes){
		boolean statfg = false;
		if(statnodes==null || !statnodes.isArray()){
			return false;
		}
		Iterator<JsonNode> nodeitor = statnodes.iterator();
		while (nodeitor.hasNext()) {
			JsonNode node = nodeitor.next();

			if (!node.get("type").isTextual() || !node.get("content").isArray()) {
				return false;
			}
			String type = node.get("type").asText();
			Iterator<JsonNode> content = node.get("content").iterator();
			int size = node.get("content").size();
			if (type.equals("termstat")) {
				intermsStat = new InTermFieldStat[size];
				resultjsongen.outtermStat = new OutTermFieldStat[size];
			} else if (type.equals("kvstat")) {
				inkvsStat = new InTermsKVStat[size];
				resultjsongen.outkvsStat = new OutTermsKVStat[size];
			} else if (type.equals("fieldstat")) {
				infieldStat = new InNumFieldStat[size];
				resultjsongen.outfieldStat = new OutFieldStat[size];
			} else if (type.equals("rangestat")) {
				inrangegroupsStat = new InRangesGroupStat[size];
				resultjsongen.outrangegroupsStat = new OutRangesGroupStat[size];
			}

			int counter = 0;
			JsonNode contentItem;
			while (content.hasNext()) {
				contentItem = content.next();
				if (contentItem.isObject() && type.equals("termstat")) {
					String name = contentItem.get("name").asText();
					intermsStat[counter] = new InTermFieldStat();
					intermsStat[counter].setName(name);

					resultjsongen.outtermStat[counter] = new OutTermFieldStat();
					resultjsongen.outtermStat[counter].setName(name);
					counter++;
					statfg=true;
				} else if (contentItem.isObject() && type.equals("kvstat")) {
					String fieldname = contentItem.get("keyfield").asText();
					String valuename = contentItem.get("valuefield").asText();
					inkvsStat[counter] = new InTermsKVStat();
					inkvsStat[counter].setKeystatfield(fieldname);
					inkvsStat[counter].setValuestatfield(valuename);
					inkvsStat[counter].setMethod(methodJsonTrans(contentItem.get("methods")));

					resultjsongen.outkvsStat[counter] = new OutTermsKVStat();
					resultjsongen.outkvsStat[counter].setName("TermKV_"+fieldname + "_" + valuename);
					counter++;
					statfg=true;
				} else if (contentItem.isObject() && type.equals("fieldstat")) {
					String name = contentItem.get("name").asText();
					infieldStat[counter] = new InNumFieldStat();
					infieldStat[counter].setName(name);
					infieldStat[counter].setMethod(methodJsonTrans(contentItem.get("methods")));

					resultjsongen.outfieldStat[counter] = new OutFieldStat();
					resultjsongen.outfieldStat[counter].setName(name);
					counter++;
					statfg=true;
				} else if (contentItem.isObject() && type.equals("rangestat")) {
					String name = contentItem.get("name").asText();
					inrangegroupsStat[counter] = new InRangesGroupStat();
					inrangegroupsStat[counter].setName(name);
					inrangegroupsStat[counter].setMethod(methodJsonTrans(contentItem.get("methods")));
					inrangegroupsStat[counter].setRanges(rangeJsonTrans(contentItem.get("ranges")));

					resultjsongen.outrangegroupsStat[counter] = new OutRangesGroupStat();
					resultjsongen.outrangegroupsStat[counter].setName(name);
					counter++;
					statfg=true;
				}
			}
		}

		return statfg;
	}
	
//	public static void main(String[] args){
//		@SuppressWarnings("rawtypes")
//		
//		ObjectMapper mapper = new ObjectMapper();
//		JsonNode rootnode=null;
//		try {
//			rootnode = mapper.readTree(new File("F:\\inputjson.json"));
//		} catch (JsonProcessingException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//			return;
//		} catch (IOException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//			return;
//		}
//		
//		SearchModeJsonGenerater gen = new SearchModeJsonGenerater();
//		SearchModeJsonParser sparser = new SearchModeJsonParser(gen);
//		sparser.parseInput(rootnode);
////		sparser.psrseOutput(rootnode);
//		sparser.parseStat(rootnode);
//		
//		sparser.testPrint();
//	}
	
	
	private ArrayList<String> mutlivalueJsonTrans(JsonNode arrnode){
		if(!arrnode.isArray()){
			return null;
		}

		Iterator<JsonNode> nodeitor = arrnode.iterator();
		int size = arrnode.size();
		ArrayList<String> valueslist = new ArrayList<String>(size);
		
		while(nodeitor.hasNext()){
			JsonNode valueItem = nodeitor.next();
			if (valueItem.isTextual()) {
				valueslist.add(valueItem.asText());
			}
		}
	
		if(valueslist.size()!=size){
			LOG.error("string2bean, value[json] to list[string] illegal !");
		}
		return valueslist;
	}
	
	private ArrayList<Integer> methodJsonTrans(JsonNode arraynode){
		if(!arraynode.isArray())
			return null;
		Iterator<JsonNode> nodeitor = arraynode.iterator();
		int size = arraynode.size();
		ArrayList<Integer> methodslist = new ArrayList<Integer>(size);
		
		while(nodeitor.hasNext()){
			JsonNode IntItem = nodeitor.next();
			if(IntItem.isInt()){
				methodslist.add(IntItem.asInt());
			}
		}
		
		if(methodslist.size()!=size){
			LOG.error("string2bean, method[json] to list[integer] illegal !");
		}
		return methodslist;
	}
	
	private ArrayList<FromTo> rangeJsonTrans(JsonNode arraynode){
		if(!arraynode.isArray())
			return null;
		Iterator<JsonNode> nodeitor = arraynode.iterator();
		int size = arraynode.size();
		ArrayList<FromTo> fromtolist = new ArrayList<FromTo>(size);
		
		while(nodeitor.hasNext()){
			JsonNode fromtoItem = nodeitor.next();
			if(fromtoItem.isObject()){
				FromTo ft = new FromTo();
				if(fromtoItem.get("from")!=null){
					ft.setFrom(fromtoItem.get("from").asText());
				}
				if(fromtoItem.get("to")!=null){
					ft.setTo(fromtoItem.get("to").asText());
				}
				
				fromtolist.add(ft);
			}
		}
		
		if(fromtolist.size()!=size){
			LOG.error("string2bean, fromto[json] to list[FromTo] illegal!");
		}
		return fromtolist;
	}
	
	private ArrayList<PositionCycle> positionJsonTrans(JsonNode arraynode){
		if(!arraynode.isArray())
			return null;
		Iterator<JsonNode> nodeitor = arraynode.iterator();
		int size = arraynode.size();
		ArrayList<PositionCycle> positioncyclelist = new ArrayList<PositionCycle>(size);
		
		while(nodeitor.hasNext()){
			JsonNode cycleItem = nodeitor.next();
			if(cycleItem.isObject()){
				PositionCycle cycle = new PositionCycle();
				cycle.setLat(cycleItem.get("lat").asDouble());
				cycle.setLon(cycleItem.get("lon").asDouble());
				cycle.setRadius(cycleItem.get("radius").asDouble());
				positioncyclelist.add(cycle);
			}
		}
		
		if(positioncyclelist.size()!=size){
			LOG.error("string2bean, latlon[json] to list[PositionCycle] illegal!");
		}
		return positioncyclelist;
	}
	
	public boolean isBulkfg() {
		return bulkfg;
	}
	
	public boolean isTotalflg() {
		return totalflg;
	}
	public boolean isKeyflg() {
		return keyflg;
	}
	public boolean isRankingflg() {
		return rankingflg;
	}
	public boolean isInputflg() {
		return inputflg;
	}

	public boolean isOutputflg() {
		return outputflg;
	}

	public boolean isStatflg() {
		return statflg;
	}
	
	public String getKey() {
		return key;
	}
	
//	public String[] getRankingfields() {
//		return rankingfields;
//	}
	
	public String[] getOutfields() {
		return outfields;
	}
	
	public IdsQueryParam[] getIdsParam() {
		return idsParam;
	}
	
	public KeyWordQueryParam[] getKwsParam() {
		return kwsParam;
	}

	public PrefixQueryParam[] getPresParam() {
		return presParam;
	}

	public RangeQueryParam[] getRangesParam() {
		return rangesParam;
	}

	public RegexQueryParam[] getRegexParam() {
		return regexParam;
	}

	public GEOQueryParam[] getGeosParam() {
		return geosParam;
	}

	public InTermFieldStat[] getIntermsStat() {
		return intermsStat;
	}

	public InTermsKVStat[] getInkvsStat() {
		return inkvsStat;
	}

	public InNumFieldStat[] getInfieldStat() {
		return infieldStat;
	}
	
	public InRangesGroupStat[] getInrangegroupsStat() {
		return inrangegroupsStat;
	}
	
	public void testPrint(){
		for(IdsQueryParam id : idsParam){
			System.out.println(id.toString());
		}
		
		for(KeyWordQueryParam kw : kwsParam){
			System.out.println(kw.toString());
		}
		
		for(PrefixQueryParam pre : presParam){
			System.out.println(pre.toString());
		}
		
		for(RangeQueryParam range : rangesParam){
			System.out.println(range.toString());
		}
		
		for(InTermFieldStat stat: intermsStat){
			System.out.println(stat.toString());
		}
		
		for(InTermsKVStat stat: inkvsStat){
			System.out.println(stat.toString());
		}
		
		for(InNumFieldStat stat: infieldStat){
			System.out.println(stat.toString());
		}
		
		for(InRangesGroupStat stat: inrangegroupsStat){
			System.out.println(stat.toString());
		}
		
		for(GEOQueryParam geo : geosParam){
			System.out.println(geo.toString());
		}
	}
}
